package search;

import act.End;
import act.IActor;
import act.IReceiver;

/**
 * Actor to join End messages This will relay all messages to the target except end messages. 
 * It will accumulate End messages and only send one to the target when it has received an end message from every 
 * client from the point of view of data flow you need one of these whenever you paralellize something.
 * 
 * <pre>
 * 
 *                                     +---------+
 *                               +---->| act1    +--+
 *                               |     +---------+  |
 *    +--------+   +--------+    |                  |   +----------+     +-------+
 *    |  src   |-->|splitter+----+                  +-->| EndJoin  +---->|  trg  |
 *    +--------+   +--------+    |                  |   +----------+     +-------+
 *                               |     +---------+  |
 *                               +---->| act2    +--+
 *                                     +---------+
 * </pre>
 * 
 *  As in the diagram above whenever you split something you need to join it again after just as you would 
 *  when wiring a circuit. You cannot join directly at the target. Instead you must use an EndJoin as
 *  a kind of junction box. You then point its single output at the target. 
 *  
 * @author poconnor 
 * 
 */
public class EndJoin implements IReceiver{

	private final int numSources;
	private final IActor target;
	private int endsReceived = 0;
	private IActor actor = null;
	
	/**
	 * @param numSources  The number of sources that will be sending messages to 
	 * this object. 
	 * @param target The target object. See class comments for explanatnion
	 */
	public EndJoin(int numSources, IActor target)
	{
		this.numSources = numSources;
		this.target = target;
	}
	
	@Override
	public void receive(Object msg) {
		
		//Forward messages from all sources to the target except End messages		
		if(msg instanceof End)
		{
			endsReceived++;
			if(endsReceived == numSources)
			{
				//all sources have ended so send an end message to the target and finish up
				target.send(new End());
				this.actor.stop();
			}
		}
		else
		{
			target.send(msg);
		}
	}

	@Override
	public void setActor(IActor actor) {
		this.actor = actor;
	}

}
